package com.lemon.reggie.service.impl;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.util.StrUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.lemon.reggie.common.BusinessException;
import com.lemon.reggie.mapper.DishFlavorMapper;
import com.lemon.reggie.pojo.dto.AddDishDTO;
import com.lemon.reggie.pojo.dto.DishFlavorDTO;
import com.lemon.reggie.pojo.dto.EditDishDTO;
import com.lemon.reggie.pojo.entity.Category;
import com.lemon.reggie.pojo.entity.Dish;
import com.lemon.reggie.mapper.DishMapper;
import com.lemon.reggie.pojo.entity.DishFlavor;
import com.lemon.reggie.pojo.vo.DishFlavorVO;
import com.lemon.reggie.pojo.vo.DishVO;
import com.lemon.reggie.pojo.vo.GetDishVO;
import com.lemon.reggie.service.CategoryService;
import com.lemon.reggie.service.DishFlavorService;
import com.lemon.reggie.service.DishService;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.exception.ExceptionUtils;
import org.springframework.cache.annotation.CacheEvict;
import org.springframework.cache.annotation.Cacheable;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.annotation.Resource;
import java.util.Collections;
import java.util.List;
import java.util.Objects;
import java.util.Set;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;

/**
 * <p>
 * 菜品管理 服务实现类
 * </p>
 *
 * @author Lemon
 * @since 2022-05-23
 */
@Slf4j
@Service
public class DishServiceImpl extends ServiceImpl<DishMapper, Dish> implements DishService {

    @Resource
    private DishFlavorService dishFlavorService;
    @Resource
    private CategoryService categoryService;
    @Resource
    private DishFlavorMapper dishFlavorMapper;
    @Resource
    private DishMapper dishMapper;
    @Resource
    private RedisTemplate<String, Object> redisTemplate;

    /**
     * 新增菜品
     *
     * @param addDishDTO
     * @return void
     * @author Guannankun
     * @since 2022/8/15
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public void addDish(AddDishDTO addDishDTO) {
        Dish one = dishMapper.findOne(addDishDTO.getName());
        /*Dish one = this.getOne(new LambdaQueryWrapper<Dish>()
                .eq(Dish::getName, addDishDTO.getName())
                .eq(Dish::getIsDeleted, 1));*/
        if (one != null) {
            //已经逻辑删除了该菜品
            //真正物理删除
            dishMapper.deleteDishById(one.getId());
            dishFlavorMapper.deleteByDishId(one.getId());
            final String dishKey = "dish_" + one.getCategoryId();
            redisTemplate.delete(dishKey);
        }

        //1.首先保存菜品Dish
        Dish dish = Dish.builder()
                .name(addDishDTO.getName())
                .categoryId(Long.valueOf(addDishDTO.getCategoryId()))
                .image(addDishDTO.getImage())
                .code(addDishDTO.getCode())
                .description(addDishDTO.getDescription())
                .price(addDishDTO.getPrice())
                .status(addDishDTO.getStatus())
                .build();
        this.save(dish);

        //2.保存菜品关联的口味
        this.saveFlavors(addDishDTO.getFlavors(), dish);

        //删除缓存中当前分类下菜品的数据
        final String dishKey = "dish_" + dish.getCategoryId();
        redisTemplate.delete(dishKey);

        /*if (CollUtil.isNotEmpty(addDishDTO.getFlavors())) {
            List<DishFlavor> dishFlavorList = addDishDTO.getFlavors().stream()
                    .map(dishFlavorDTO -> DishFlavor.builder()
                            .dishId(dish.getId())
                            .name(dishFlavorDTO.getName())
                            .value(dishFlavorDTO.getValue())
                            .build()).collect(Collectors.toList());
            dishFlavorService.saveBatch(dishFlavorList);
        }*/
    }

    /**
     * 菜品分页
     * @param currentPage
     * @param pageSize
     * @param name
     * @return com.baomidou.mybatisplus.core.metadata.IPage<com.lemon.reggie.pojo.vo.DishVO>
     * @author Guannankun
     * @since 2022/8/15
     */
    @Override
    public IPage<DishVO> pageDish(Long currentPage, Long pageSize, String name) {
        IPage<Dish> iPage = new Page<>(currentPage, pageSize);

        IPage<Dish> dishPage = this.page(iPage, new LambdaQueryWrapper<Dish>()
                .like(StrUtil.isNotBlank(name), Dish::getName, name)
                .orderByDesc(Dish::getUpdateTime));

        List<DishVO> dishVOS = dishPage.getRecords().stream()
                .map(dish -> DishVO.builder()
                        .id(dish.getId())
                        .name(dish.getName())
                        .image(dish.getImage())
                        .categoryId(String.valueOf(dish.getCategoryId()))
                        .categoryName(this.getCategoryName(dish.getCategoryId()))
                        .price(dish.getPrice())
                        .status(dish.getStatus())
                        .updateTime(dish.getUpdateTime())
                        .build()).collect(Collectors.toList());

        IPage<DishVO> dishVOPage = new Page<>();
        //对象拷贝，忽略records记录数
        BeanUtil.copyProperties(dishPage, dishVOPage, "records");
        dishVOPage.setRecords(dishVOS);
        return dishVOPage;
    }

    /**
     * 删除菜品
     * @param ids
     * @return void
     * @author Guannankun
     * @since 2022/8/15
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public void deleteDish(List<Long> ids) {
        List<Dish> list = this.list(new LambdaQueryWrapper<Dish>().eq(Dish::getStatus, 1).in(Dish::getId, ids));
        if (CollUtil.isNotEmpty(list)) {
            throw new BusinessException("有菜品正在启售，不能删除！");
        }

        //1.先删除菜品口味
        if (CollUtil.isNotEmpty(ids)) {
            //List<DishFlavor> dishFlavorList = dishFlavorMapper.findByDishIds(ids);
            List<DishFlavor> dishFlavorList = dishFlavorService.list(new LambdaQueryWrapper<DishFlavor>()
                    .in(DishFlavor::getDishId, ids));
            if (CollUtil.isNotEmpty(dishFlavorList)) {
                List<Long> dishFlavors = dishFlavorList.stream()
                        .map(DishFlavor::getId).collect(Collectors.toList());
                dishFlavorService.removeByIds(dishFlavors);
            }
            //2.删除菜品
            this.removeByIds(ids);

            //3.清除缓存中所有菜品的数据
            Set<String> dishKeys = redisTemplate.keys("dish_*");
            if (CollUtil.isNotEmpty(dishKeys)) {
                redisTemplate.delete(dishKeys);
            }
        }
    }

    /** 启售/停售
     * @param status
     * @param ids
     * @return void
     * @author Guannankun
     * @since 2022/8/16
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public void statusDish(Integer status, List<Long> ids) {
        List<Dish> dishList = this.listByIds(ids);
        if (CollUtil.isNotEmpty(dishList)) {
            dishList.forEach(dish-> dish.setStatus(status));
            this.updateBatchById(dishList);
        }
        //清除缓存中所有菜品的数据
        Set<String> dishKeys = redisTemplate.keys("dish_*");
        if (CollUtil.isNotEmpty(dishKeys)) {
            redisTemplate.delete(dishKeys);
        }
    }

    /**
     * 根据Id获取菜品
     * @param id
     * @return com.lemon.reggie.pojo.vo.GetDishVO
     * @author Guannankun
     * @since 2022/8/16
     */
    @Cacheable(value = "dish", key = "#id", unless = "#result == null")
    @Override
    public GetDishVO getDish(Long id) {
        Dish dish = this.getById(id);
        if (Objects.isNull(dish)) {
            throw new BusinessException("当前菜品不存在！");
        }

        GetDishVO getDishVO = GetDishVO.builder()
                .id(id)
                .categoryId(String.valueOf(dish.getCategoryId()))
                .categoryName(this.getCategoryName(dish.getCategoryId()))
                .name(dish.getName())
                .price(dish.getPrice())
                .image(dish.getImage())
                .description(dish.getDescription())
                .status(dish.getStatus())
                .build();

        List<DishFlavor> dishFlavorList = dishFlavorService.list(new LambdaQueryWrapper<DishFlavor>()
                .eq(DishFlavor::getDishId, id));

        if (CollUtil.isNotEmpty(dishFlavorList)) {
            List<DishFlavorDTO> dishFlavorDTOS =
                    dishFlavorList.stream().map(dishFlavor -> DishFlavorDTO.builder()
                            .dishId(dishFlavor.getDishId())
                            .name(dishFlavor.getName())
                            .value(dishFlavor.getValue())
                            .build())
                            .collect(Collectors.toList());
            getDishVO.setFlavors(dishFlavorDTOS);
        }
        return null;
        //return getDishVO;
    }

    /**
     * 修改菜品
     * @param editDishDTO
     * @return void
     * @author Guannankun
     * @since 2022/8/17
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public void editDish(EditDishDTO editDishDTO) {
        //1.根据菜品名称查询已经逻辑删除的数据是否存在，存在就真的物理删除
        Dish dish = dishMapper.findEditOne(editDishDTO);
        if (dish != null) {
            //已经逻辑删除了该菜品
            //真正物理删除
            dishMapper.deleteDishById(dish.getId());
            dishFlavorMapper.deleteByDishId(dish.getId());

            //清除缓存
            final String dishKey = "dish_" + dish.getCategoryId();
            redisTemplate.delete(dishKey);
        }

        //1. 先修改菜品信息
        Dish one = this.getById(editDishDTO.getId());
        BeanUtil.copyProperties(editDishDTO, one);
        this.updateById(one);

        //修改菜品口味信息
        //1.先删除菜品口味信息
        List<DishFlavor> dishFlavorList = dishFlavorService.list(new LambdaQueryWrapper<DishFlavor>()
                .eq(DishFlavor::getDishId, editDishDTO.getId()));

        if (CollUtil.isNotEmpty(dishFlavorList)) {
            List<Long> flavorIds = dishFlavorList.stream().map(DishFlavor::getId).collect(Collectors.toList());
            dishFlavorService.removeByIds(flavorIds);
        }

        //2.添加菜品口味信息
        this.saveFlavors(editDishDTO.getFlavors(), one);


        //3. 删除缓存中当前分类下菜品的数据
        final String dishKey = "dish_" + one.getCategoryId();
        redisTemplate.delete(dishKey);
    }

    /**
     * 新增套餐时获取菜品集合
     *
     * @param categoryId
     * @param name
     * @return java.util.List<com.lemon.reggie.pojo.vo.DishVO>
     * @author Guannankun
     * @since 2022/8/17
     */
    @Override
    //@Cacheable(value = "dish", key = "#categoryId")
    public List<DishVO> listDish(Long categoryId, String name) {
        if (StrUtil.isBlank(name) && Objects.isNull(categoryId)) {
            throw new BusinessException("菜品分类id不能为空！");
        }

        //PC和移动端都适用，所以对移动端首页进行缓存优化
        if (Objects.nonNull(categoryId)) {
            List<DishVO> dishVOS = null;
             final String dishKey = "dish_" + categoryId;

            try {
                //缓存中有，直接返回
                dishVOS = (List<DishVO>) redisTemplate.opsForValue().get(dishKey);
                if (CollUtil.isNotEmpty(dishVOS)) {
                    return dishVOS;
                }
            } catch (Exception e) {
                e.printStackTrace();
                log.error("redis异常...{}", ExceptionUtils.getStackTrace(e));
            }

            List<Dish> dishList = this.list(new LambdaQueryWrapper<Dish>()
                    .eq(Dish::getCategoryId, categoryId)
                    .eq(Dish::getStatus, 1)
                    .eq(Dish::getIsDeleted, 0)
                    .orderByDesc(Dish::getUpdateTime));
            if (CollUtil.isNotEmpty(dishList)) {
                dishVOS = this.dish2VO(dishList);
                try {
                    //存入缓存，1小时
                    redisTemplate.opsForValue().set(dishKey, dishVOS, 1, TimeUnit.HOURS);
                } catch (Exception e) {
                    e.printStackTrace();
                    log.error("redis异常...{}", ExceptionUtils.getStackTrace(e));
                }
                return dishVOS;
            }
        }

        if (StrUtil.isNotBlank(name)) {
            List<Dish> dishList = this.list(new LambdaQueryWrapper<Dish>()
                    .like(Dish::getName, name)
                    .eq(Dish::getStatus, 1)
                    .eq(Dish::getIsDeleted, 0)
                    .orderByDesc(Dish::getUpdateTime));
            if (CollUtil.isNotEmpty(dishList)) {
                return this.dish2VO(dishList);
            }
        }

        return Collections.emptyList();
    }

    /**
     * dish2VO
     * @param dishList
     * @return java.util.List<com.lemon.reggie.pojo.vo.DishVO>
     * @author Guannankun
     * @since 2022/8/17
     */
    private List<DishVO> dish2VO(List<Dish> dishList) {
        return dishList.stream().map(dish -> DishVO.builder()
                .id(dish.getId())
                .categoryId(String.valueOf(dish.getCategoryId()))
                .categoryName(this.getCategoryName(dish.getCategoryId()))
                .name(dish.getName())
                .price(dish.getPrice())
                .image(dish.getImage())
                .status(dish.getStatus())
                .flavors(this.getDishFlavorVOS(dish.getId()))
                .build())
                .collect(Collectors.toList());
    }

    /**
     * 获取菜品名称
     * @param categoryId
     * @return java.lang.String
     * @author Guannankun
     * @since 2022/8/15
     */
    private String getCategoryName(Long categoryId) {
        Category one = categoryService.getOne(new LambdaQueryWrapper<Category>()
                .eq(Category::getType, 1)
                .eq(Category::getId, categoryId));
        return one.getName();
    }

    /**
     * 保存菜品口味
     * @param flavors
     * @param dish
     * @return void
     * @author Guannankun
     * @since 2022/8/17
     */
    private void saveFlavors(List<DishFlavorDTO> flavors, Dish dish) {
        //2.保存菜品关联的口味
        if (CollUtil.isNotEmpty(flavors)) {
            List<DishFlavor> dishFlavorList = flavors.stream()
                    .map(dishFlavorDTO -> DishFlavor.builder()
                            .dishId(dish.getId())
                            .name(dishFlavorDTO.getName())
                            .value(dishFlavorDTO.getValue())
                            .build()).collect(Collectors.toList());
            dishFlavorService.saveBatch(dishFlavorList);
        }
    }

    /**
     * 根据菜品id获取口味数据
     * @param dishId
     * @return java.util.List<com.lemon.reggie.pojo.vo.DishFlavorVO>
     * @author Guannankun
     * @since 2022/8/22
     */
    private List<DishFlavorVO> getDishFlavorVOS(Long dishId) {
        List<DishFlavor> dishFlavorList = dishFlavorService.list(new LambdaQueryWrapper<DishFlavor>()
                .eq(DishFlavor::getDishId, dishId));
        if (CollUtil.isNotEmpty(dishFlavorList)) {
            return dishFlavorList.stream().map(dishFlavor -> DishFlavorVO.builder().id(dishFlavor.getId())
                    .name(dishFlavor.getName())
                    .value(dishFlavor.getValue())
                    .build()).collect(Collectors.toList());
        }
        return Collections.emptyList();
    }
}
